/**
 * @brief 3.5.3 指针和数组
 * 在C++语言中，指针和数组有非常紧密的联系，使用数组的时候编译器一般会把它转换成指针
 * 像其他对象一样，对数组的元素使用取地址符就能得到指向该元素的指针
 */
#include <iostream>
#include <string>
#include <cstddef>
#include <iterator> // iterator定义了begin和end函数，用于得到数组的首元素指针和尾后指针
using std::begin, std::end;
using std::cin, std::cout, std::endl;
using std::size_t; // 在cstddef头文件中定义了size_t类型
using std::string;

int main(int argc, char const *argv[])
{
    // 数组的元素也是对象，使用取地址符&就能得到指向该数组的指针
    string nums[] = {"one", "two", "three"};
    string *p = &nums[0];     // p指向nums的第一个元素
    string(*parr)[3] = &nums; // *parr是数组的指针，指向含有3个string型的数组

    // 数组还有一个特性：在很多用到数组名字的地方，编译器都会自动地将其替换为一个指向数组首元素的指针
    string *p1 = nums; // 等价于string *p1 = &num[0];
    auto *p2(nums);    // *p2被auto修饰，被编译器推断出来是个指针，指向nums的第一个元素

    // 当使用decltype时上述转换不会发生
    decltype(nums) nums1 = {"for", "five", "six"}; // nums1与nums类型一致
    nums1[0] = "four";

    // 指针也是迭代器
    // 例如，允许使用递增运算符将指向数组元素的指针向前移动到下一个位置上
    int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    int *p11 = arr; // p11指向arr的第一个元素
    ++p11;          // p11指向arr[1]

    // 使用指针循环数组
    int *e = &arr[10]; // 指向arr尾元素的下一位置的指针
    for (int *b = arr; b != e; ++b)
    {
        cout << *b << endl; // 输出arr的元素
    }

    // iterator头文件中定义了begin和end函数，用于得到数组的首元素指针和尾后指针
    int *pbeg = begin(arr), *pend = end(arr); // 需要using std::begin, std::end;
    // 注意不能对尾后指针进行解引用操作

    // 指针运算，就如同迭代器运算一样
    // 和迭代器一样，两个指针相减的结果是它们之间的距离
    auto n = pend - pbeg; // 两个指针相减的结果的类型为ptrdiff_t的标准库类型
    // 和size_t一样，ptrdiff_t也是一种定义在cstddef头文件中的机器相关的类型
    // 因为差值可能为负值，所以ptrdiff_t是一种带符号类型。

    // 下标和指针
    // 只要指针指向的是数组中的元素（或者数组中尾元素的下一位置），都可以执行下标运算
    int ia[] = {0, 2, 4, 6, 8};
    int *pi = &ia[2]; // pi指向索引为2的元素
    int j = pi[1]; // pi[1]等价于*(pi + 1)，就是ia[3]表示的那个元素
    int k = pi[-2]; // pi[-2]是ia[0]表示的那个元素

    return 0;
}
